#include "tcpclient.h"
#include "asynevent.h"
#include "ntdtime.h"

//static void cb_func(evutil_socket_t fd, short what, void *user_data)
//{
//	if( what&EV_READ ){
//		(static_cast<TCPClient *>(user_data))->OnRead();
//	}
//
//	if( what&EV_WRITE ){
//		(static_cast<TCPClient *>(user_data))->OnWrite();
//	}
//}

static void timeout_cb(evutil_socket_t fd, short event, void *user_data)
{
	(static_cast<TCPClient *>(user_data))->OnTimer();
}

static void readcb(struct bufferevent *bev, void *user_data)
{
	(static_cast<TCPClient *>(user_data))->OnRead();
}

static void writecb(struct bufferevent *bev, void *user_data)
{
	(static_cast<TCPClient *>(user_data))->OnWrite();
}

static void eventcb(struct bufferevent *bev, short events, void *user_data)
{
	 if (events & BEV_EVENT_CONNECTED) {
		 printf("connected!\n");
		 (static_cast<TCPClient *>(user_data))->OnConnected();
	 }else if (events & (BEV_EVENT_ERROR)){
		 (static_cast<TCPClient *>(user_data))->OnError();
     }else if (events & (BEV_EVENT_EOF)){
		 (static_cast<TCPClient *>(user_data))->OnClose();
	 }
	 //else if (events & (BEV_EVENT_TIMEOUT)){
		// (static_cast<TCPClient *>(user_data))->OnTimer();
	 //}
}

TCPClient::TCPClient()
{
	m_base = AsynEventFramework::Instance()->GetEventBase();

	m_seq = 0;

	m_owd_base = 0;

	m_protodata.m_len = MSS_SIZE;
	m_protodata.m_pData = new byte[MSS_SIZE];
	memset(m_protodata.m_pData, 0, m_protodata.m_len*sizeof(byte));

	m_lastsent = 0;

	SetRateLimit(UINT_MAX);

	///* Initalize one event */
	//event_assign(&m_timeout, m_base, -1, EV_PERSIST, timeout_cb, this);

	//struct timeval tv;
	//evutil_timerclear(&tv);
	//tv.tv_sec = 1;
	//event_add(&m_timeout, &tv);
}

TCPClient::~TCPClient()
{
	Close();

	//event_free(m_timeout);
	delete []m_protodata.m_pData;
}

bool TCPClient::Connect(const std::string& ip, int port)
{
	struct sockaddr_in sin;	
	SetSockAddr(sin, ntohl(inet_addr(ip.c_str())), port);

	m_bev = bufferevent_socket_new(m_base, -1, BEV_OPT_CLOSE_ON_FREE);

	int ret = bufferevent_socket_connect(m_bev, (sockaddr*)&sin, sizeof(sockaddr));

	bufferevent_setcb(m_bev, readcb, writecb, eventcb, this);

	bufferevent_enable(m_bev, EV_READ|EV_WRITE);

	//struct timeval tv = {0, 10000};
	//bufferevent_set_timeouts(m_bev, &tv, &tv);

	return ret==0?true:false;
}

void TCPClient::OnConnected()
{
	//bufferevent_write(m_bev, MESSAGE, strlen(MESSAGE));
	SendData();
}

void TCPClient::Close()
{
	bufferevent_free(m_bev);
}

void TCPClient::OnTimer()
{
	SendData();
}

void TCPClient::OnWrite()
{
	//Sleep(500);
	if( NtdTime::Instance()->GetCurrentTime_us() > m_lastsent + m_pktInterval ){
		SendData();
	}
}

void TCPClient::SetRateLimit(uint32 limit)
{
	m_ratelimit = limit;
	m_pktInterval = 1000000 * MSS_SIZE / m_ratelimit;
}

void TCPClient::SendData()
{
	m_lastsent = NtdTime::Instance()->GetCurrentTime_us();

	m_seq = NtdSeq::incseq(m_seq);
	m_protodata.m_ptk_seq = m_seq;
	m_protodata.m_timestamp_sending = NtdTime::Instance()->GetCurrentTime_ms();
	struct evbuffer *output = bufferevent_get_output(m_bev);
	m_protodata.Encode(output);

	//LOG4CPLUS_TRACE(g_logger,m_protodata.Desc());
}

void TCPClient::OnRead()
{
	//char buf[1024];
	//int n;
	//struct evbuffer *input = bufferevent_get_input(m_bev);
	//while ((n = evbuffer_remove(input, buf, sizeof(buf))) > 0) {
	//	fwrite(buf, 1, n, stdout);
	//}

	while(true){
		bool ret = NtdProtoParser::ParseProtocol(bufferevent_get_input(m_bev), NULL);
		if( ret == false ){
			return;
		}
	}
}

void TCPClient::OnError()
{

}

void TCPClient::HandleProtoFeedback(NtdProtoFeedback * proto, void* pContext)
{
	//return;
	//LOG4CPLUS_TRACE(g_logger,proto->Desc());
	uint32 now = NtdTime::Instance()->GetCurrentTime_ms();
	uint32 rtt = now - proto->m_timestamp_sending;

	int32 owd = proto->m_timestamp_recving - proto->m_timestamp_sending;
	if(m_owd_base == 0){
		m_owd_base = owd;
	}

	int32 owd_var = owd - m_owd_base;

	LOG4CPLUS_DEBUG(g_logger, "now = "<< now <<" ms, send_ts = "<<proto->m_timestamp_sending
		<< " ms, rtt = " << rtt <<" ms, owd_var = " << owd_var <<" ms " 
		<<" recv_rate = " << proto->m_recving_rate << "Bytes/sec");
}

void TCPClient::OnClose()
{

}